package com.xframe.xdal.core.model.sqlbuilder;


import com.xframe.xdal.core.constant.DalConstants;
import com.xframe.xdal.core.constant.DataSourceTypeEnums;
import com.xframe.xdal.core.constant.SqlBuilderConstants;
import com.xframe.xdal.core.exception.SqlBuilderException;
import com.xframe.xdal.core.handler.HandlerColumn;
import com.xframe.xdal.core.handler.HandlerColumnMapManage;
import com.xframe.xdal.core.model.mapping.*;
import com.xframe.xdal.core.util.StringUtil;
import com.xframe.xdal.core.util.XReflect;
import lombok.Data;
import lombok.extern.log4j.Log4j2;

import java.util.List;

/**
 * 功能描述:基础的SQL构造器
 *
 * @author xufeng
 * @version 1.0
 * @date 2022-09-26
 */
@Data
@Log4j2
public abstract class BaseSqlBuilder {



    public void beforeBuild(BuilderContext context){

    }

    public void after(BuilderContext context,SqlExpression sqlExpression){

    }

    public SqlExpression run(BuilderContext context){
        try {
            this.beforeBuild(context);
            SqlExpression sqlExpression = this.build(context);
            this.after(context,sqlExpression);
            return sqlExpression;
        }catch (Exception exception){
            throw new SqlBuilderException(exception.getMessage());
        }

    }

    /**
     * 构建sql表达式对象
     *
     * @param context 上下文
     * @return SqlExpression
     */
    public abstract SqlExpression build(BuilderContext context);

    /**
     * 构建sql表达式对象
     *
     * @param colPlaceholder 占位符
     * @param tableInfo 表信息
     * @return  >>>>>>>  `t01_city` as `city`
     */
    protected void buildMainSelect(BuilderContext context,StringBuilder colstrBuilder,
                                   String colPlaceholder, ITableInfo tableInfo, HandlerColumnMapManage handlerColumnMapManage){
        // 处理主表 列名称
        int index = 0;
        for(IColumnMapping columnMapping : tableInfo.getColumnMappingList()){


            String alName = SqlBuilderConstants.MAINTABELNAME + SqlBuilderConstants.SP + columnMapping.getFieldName();
            if(context.getDataSourceTypeEnums() == DataSourceTypeEnums.ClickHouse){
                alName = "\"" + alName + "\"";
            }
            handlerColumnMapManage.put(index + 1,new HandlerColumn(index + 1,"",alName, columnMapping));
            String tmp = colPlaceholder + SqlBuilderConstants.MAINTABELNAME + colPlaceholder + SqlBuilderConstants.SP +
                    colPlaceholder + context.getNameConvert().toName(columnMapping.getName()) + colPlaceholder + SqlBuilderConstants.AS +
                    colPlaceholder + alName + colPlaceholder;
            colstrBuilder.append(tmp);
            if (index + 1 < tableInfo.getColumnMappingList().size()) {
                colstrBuilder.append(DalConstants.SQL_SEPARATION);
            }
            index++;
        }
    }


    /**
     * 构建sql表达式对象
     *
     * @param context 运行时
     * @param colstrBuilder 列信息
     * @return  >>>>>>>  `t01_city` as `city`
     */
    protected void buildSelect(BuilderContext context,StringBuilder colstrBuilder,IRelation relation,
                               ITableInfo tableInfo, HandlerColumnMapManage handlerColumnMapManage,
                               boolean refFlag, boolean refListFlag, Class<?> dataType){
        // 处理主表 列名称
        int index = 0;
        int setIndex = handlerColumnMapManage.size();
        for(IColumnMapping columnMapping : tableInfo.getColumnMappingList()){


            String alName = relation.getAs() + SqlBuilderConstants.SP + columnMapping.getFieldName();
            if(context.getDataSourceTypeEnums() == DataSourceTypeEnums.ClickHouse){
                alName = "\"" + alName + "\"";
            }
            setIndex++;
            handlerColumnMapManage.put(setIndex,new HandlerColumn(setIndex,relation.getAs(),alName,tableInfo,relation,columnMapping,refFlag,refListFlag,dataType));

            String tmp = context.getColPlaceholder() + relation.getAs() + context.getColPlaceholder() + SqlBuilderConstants.SP +
                    context.getColPlaceholder() + context.getNameConvert().toName(columnMapping.getName()) + context.getColPlaceholder() + SqlBuilderConstants.AS +
                    context.getColPlaceholder() + alName + context.getColPlaceholder();
            colstrBuilder.append(tmp);
            if (index + 1 < tableInfo.getColumnMappingList().size()) {
                colstrBuilder.append(DalConstants.SQL_SEPARATION);
            }
            index++;
        }
    }

    /**
     * 构建sql表达式对象 from
     *
     * @param colPlaceholder 占位符
     * @param tableInfo 表信息
     * @return  >>>>>>>  `t01_city` as `this`
     */
    protected String buildFrom(BuilderContext context,String colPlaceholder, ITableInfo tableInfo){
        return colPlaceholder + context.getNameConvert().toName(tableInfo.getName()) + colPlaceholder + " "
                + colPlaceholder + SqlBuilderConstants.MAINTABELNAME + colPlaceholder;
    }

    /**
     * 构建sql表达式对象 left join tableName1 as "alTableName1" on
     *
     * @param context 上下文
     * @param colPlaceholder 占位符
     * @param maintableInfo 主表信息
     * @param tableInfo 表信息
     * @param relation 关系
     * @return left join tableName1 as "alTableName1" on
     */
    protected String buildFromJoinFrom(BuilderContext context,String colPlaceholder, ITableInfo maintableInfo,ITableInfo tableInfo,
                                       IRelation relation){
        String tmp = SqlBuilderConstants.LEFT_JOIN + colPlaceholder +
                context.getNameConvert().toName(tableInfo.getName()) + colPlaceholder + " "
                + colPlaceholder + relation.getAs() + colPlaceholder + SqlBuilderConstants.ON;

        IColumnMapping mainColumnMapping = maintableInfo.findOneByFieldName(relation.getForeignKey());

        IColumnMapping columnMapping = tableInfo.findOneByFieldName(relation.getTargetKey());
        if(mainColumnMapping == null || columnMapping == null){
            // 抛出异常
            return "";
        }
        tmp +=  colPlaceholder + SqlBuilderConstants.MAINTABELNAME + colPlaceholder +  SqlBuilderConstants.SP + colPlaceholder
                + context.getNameConvert().toName(mainColumnMapping.getName()) + colPlaceholder + SqlBuilderConstants.EQ +
                colPlaceholder + relation.getAs() + colPlaceholder + SqlBuilderConstants.SP + colPlaceholder + context.getNameConvert().toName(columnMapping.getName()) + colPlaceholder;
        if(!StringUtil.isEmpty(relation.getRefTableAddString())){
            tmp +=  " AND " + colPlaceholder + relation.getAs() + colPlaceholder + SqlBuilderConstants.SP + relation.getRefTableAddString();
        }
        return tmp;
    }


    /**
     * 根据唯一索引来构建where 查询条件和组合参数
     * @param context 上下文
     * @param param 参数
     * @return
     */
    protected StringBuilder builderWhereByUniqueKey(BuilderContext context, List<Object> param){
        StringBuilder builder = new StringBuilder();
        List<IColumnMapping> uniqueKeyList = context.findUniqueKeyList();
        for (int index = 0; index<uniqueKeyList.size();index++){
            builder.append(context.getNameConvert().toName(uniqueKeyList.get(index).getName())).append(DalConstants.SQL_EQUAL)
                    .append(DalConstants.SQL_PLACE_HOlDER);
            if(index +1 < uniqueKeyList.size()){
                builder.append(DalConstants.SQL_AND);
            }
            param.add(context.getData().getObjectFieldValue(uniqueKeyList.get(index).getFieldName()));
        }
        return builder;
    }

    /**
     * 根据唯一索引来构建where 查询条件和组合参数
     * @param context 上下文
     * @param param 参数
     * @return
     */
    protected StringBuilder builderWhereByUniqueKey(Object data,BuilderContext context, List<Object> param){
        StringBuilder builder = new StringBuilder();
        List<IColumnMapping> uniqueKeyList = context.findUniqueKeyList();
        for (int index = 0; index<uniqueKeyList.size();index++){
            IColumnMapping columnMapping = uniqueKeyList.get(index);
            builder.append(context.getNameConvert().toName(columnMapping.getName())).append(DalConstants.SQL_EQUAL)
                    .append(DalConstants.SQL_PLACE_HOlDER);
            if(index +1 < uniqueKeyList.size()){
                builder.append(DalConstants.SQL_AND);
            }
            param.add(XReflect.getFieldValue(data,columnMapping.getFieldName()));
        }
        return builder;
    }
}
